using System;
using System.Runtime.CompilerServices;
using System.Threading;

namespace UtilZ.Dotnet.Ex.Base
{
    /// <summary>
    /// 原子锁,与SpinLock类似,但是是通过Interlocked实现的.
    /// 使用方法注意事项:作为全局变量时,不能用readonly修饰.否则无效.
    /// 原因是结构体每改变一次值,都会将内存重写一遍.如果是readonly修饰,则无法修改内存,推荐使用指针方式
    /// </summary>
    public struct AtomicLock
    {
        /// <summary>
        /// 锁对象,0表示无锁,1表示已被锁
        /// </summary>
        private int _lock;


#if NET4_0
        private int GetMillisecondTicks()
        {
            return Environment.TickCount;
        }

                private bool GetLock()
        {
            return Interlocked.CompareExchange(ref this._lock, 1, 0) != 0;
        }
#else
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private int GetMillisecondTicks()
        {
            return Environment.TickCount;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private bool GetLock()
        {
            return Interlocked.CompareExchange(ref this._lock, 1, 0) != 0;
        }
#endif

        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        //private void ReleaseLock()
        //{
        //    Interlocked.CompareExchange(ref this._lock, 0, 1);
        //}




        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            this._lock = 0;
        }






        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        public void Enter()
        {
            while (this.GetLock())
            {

            }
        }

        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        /// <param name="token">取消CancellationToken</param>
        public void Enter(CancellationToken token)
        {
            while (this.GetLock())
            {
                if (token.IsCancellationRequested)
                {
                    throw new OperationCanceledException();
                }
            }
        }


        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        /// <param name="millisecondsTimeout">超时时长,为Timeout.Infinite时无限</param>
        public void Enter(int millisecondsTimeout)
        {
            if (millisecondsTimeout < 0 && millisecondsTimeout != Timeout.Infinite)
            {
                throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout));
            }

            int startTimestamp = this.GetMillisecondTicks();

            while (true)
            {
                if (this.GetLock())
                {
                    break;
                }
                else
                {
                    if (millisecondsTimeout == Timeout.Infinite)
                    {
                        continue;
                    }

                    if (this.GetMillisecondTicks() - startTimestamp > millisecondsTimeout)
                    {
                        throw new TimeoutException();
                    }
                }
            }
        }

        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        public void Enter(TimeSpan timeout)
        {
            int millisecondsTimeout = (int)timeout.TotalMilliseconds;
            int startTimestamp = this.GetMillisecondTicks();

            while (true)
            {
                if (this.GetLock())
                {
                    break;
                }
                else
                {
                    if (this.GetMillisecondTicks() - startTimestamp > millisecondsTimeout)
                    {
                        throw new TimeoutException();
                    }
                }
            }
        }

        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        /// <param name="millisecondsTimeout">超时时长,为Timeout.Infinite时无限</param>
        /// <param name="token">取消CancellationToken</param>
        public void Enter(int millisecondsTimeout, CancellationToken token)
        {
            if (millisecondsTimeout < 0 && millisecondsTimeout != Timeout.Infinite)
            {
                throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout));
            }

            int startTimestamp = this.GetMillisecondTicks();

            while (!token.IsCancellationRequested)
            {
                if (this.GetLock())
                {
                    break;
                }
                else
                {
                    if (millisecondsTimeout == Timeout.Infinite)
                    {
                        continue;
                    }

                    if (this.GetMillisecondTicks() - startTimestamp > millisecondsTimeout)
                    {
                        throw new TimeoutException();
                    }
                }
            }

            if (token.IsCancellationRequested)
            {
                throw new OperationCanceledException();
            }
        }

        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        /// <param name="timeout">超时时长</param>
        /// <param name="token">取消CancellationToken</param>
        public void Enter(TimeSpan timeout, CancellationToken token)
        {
            int millisecondsTimeout = (int)timeout.TotalMilliseconds;
            int startTimestamp = this.GetMillisecondTicks();

            while (!token.IsCancellationRequested)
            {
                if (this.GetLock())
                {
                    break;
                }
                else
                {
                    if (this.GetMillisecondTicks() - startTimestamp > millisecondsTimeout)
                    {
                        throw new TimeoutException();
                    }
                }
            }

            if (token.IsCancellationRequested)
            {
                throw new OperationCanceledException();
            }
        }




        /// <summary>
        /// 尝试获取锁,获取成功返回true,获取失败返回false
        /// </summary>
        /// <returns>获取成功返回true,获取失败返回false</returns>
        public bool TryEnter()
        {
            return this.GetLock();
        }

        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        /// <param name="millisecondsTimeout">超时时长,为Timeout.Infinite时无限</param>
        public bool TryEnter(int millisecondsTimeout)
        {
            if (millisecondsTimeout < 0 && millisecondsTimeout != Timeout.Infinite)
            {
                throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout));
            }

            int startTimestamp = this.GetMillisecondTicks();

            while (true)
            {
                if (this.GetLock())
                {
                    return true;
                }
                else
                {
                    if (millisecondsTimeout == Timeout.Infinite)
                    {
                        continue;
                    }

                    if (this.GetMillisecondTicks() - startTimestamp > millisecondsTimeout)
                    {
                        break;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        public bool TryEnter(TimeSpan timeout)
        {
            int millisecondsTimeout = (int)timeout.TotalMilliseconds;
            int startTimestamp = this.GetMillisecondTicks();

            while (true)
            {
                if (this.GetLock())
                {
                    return true;
                }
                else
                {
                    if (this.GetMillisecondTicks() - startTimestamp > millisecondsTimeout)
                    {
                        break;
                    }
                }
            }

            return false;
        }


        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        /// <param name="millisecondsTimeout">超时时长,为Timeout.Infinite时无限</param>
        /// <param name="token">取消CancellationToken</param>
        public bool TryEnter(int millisecondsTimeout, CancellationToken token)
        {
            if (millisecondsTimeout < 0 && millisecondsTimeout != Timeout.Infinite)
            {
                throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout));
            }

            int startTimestamp = this.GetMillisecondTicks();

            while (!token.IsCancellationRequested)
            {
                if (this.GetLock())
                {
                    return true;
                }
                else
                {
                    if (millisecondsTimeout == Timeout.Infinite)
                    {
                        continue;
                    }

                    if (this.GetMillisecondTicks() - startTimestamp > millisecondsTimeout)
                    {
                        break;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// 进入锁,如果已被锁,则死循环直到获取到锁
        /// </summary>
        /// <param name="timeout">超时时长</param>
        /// <param name="token">取消CancellationToken</param>
        public bool TryEnter(TimeSpan timeout, CancellationToken token)
        {
            int millisecondsTimeout = (int)timeout.TotalMilliseconds;
            int startTimestamp = this.GetMillisecondTicks();

            while (!token.IsCancellationRequested)
            {
                if (this.GetLock())
                {
                    return true;
                }
                else
                {
                    if (this.GetMillisecondTicks() - startTimestamp > millisecondsTimeout)
                    {
                        break;
                    }
                }
            }

            return false;
        }






        /// <summary>
        /// 释放锁
        /// </summary>
        public void Exit()
        {
            //如果获取到锁,则将_lock重置会0；
            Interlocked.CompareExchange(ref this._lock, 0, 1);
            //this.ReleaseLock();
            //return Interlocked.CompareExchange(ref this._lock, 0, 1) == 0;
        }

    }
}
